14.CS61C Lab1

Exercise 1: See what you can C

这个有些 C 语言基础的都很容易做出来

#include <stdio.h>

/* Only change any of these 4 values */
#define V0 3
#define V1 0
#define V2 1
#define V3 3

int main(void) {
    int a;
    char *s;

    /* This is a print statement. Notice the little 'f' at the end!
     * It might be worthwhile to look up how printf works for your future
     * debugging needs... */
    printf("Berkeley eccentrics:\n====================\n");

    /* for loop */
    for (a = 0; a < V0; a++)
    {
        printf("Happy ");
    }
    printf("\n");

    /* switch statement */
    switch (V1)
    {
    case 0:
        printf("Yoshua\n");
    case 1:
        printf("Triangle Man\n");
        break;
    case 2:
        printf("Chinese Erhu Guy\n");
    case 3:
        printf("Yoshua\n");
        break;
    case 4:
        printf("Dr. Jokemon\n");
        break;
    case 5:
        printf("Hat Lady\n");
    default:
        printf("I don't know these people!\n");
    }

    /* ternary operator */
    s = (V3 == 3) ? "Go" : "Boo";

    /* if statement */
    if (V2) {
        printf("%s BEARS!\n", s);
    } else {
        printf("%s CARDINAL!\n", s);
    }

    return 0;
}

Exercise 2: Catch those bugs!

这个练习主要是要求我们使用 gdb 工具,由于我使用的是 Mac 电脑,所以用 lldb 来代替

  1. While you’re in a gdb session, how do you set the arguments that will be passed to the program when it’s run?
    当你在 gdb 会话中时,如何设置程序运行时传递给程序的参数?

    g++ [文件名] -o [编译后的程序可执行文件名] -g
    
  2. How do you create a breakpoint?
    如何创建断点?

    (lldb) break set -f [文件名] -l [行号]
    
  3. How do you execute the next line of C code in the program after stopping at a breakpoint?
    如何在程序停止在断点后执行下一行 C 代码?

    next
    
  4. If the next line of code is a function call, you’ll execute the whole function call at once if you use your answer to #3. (If not, consider a different command for #3!) How do you tell GDB that you want to debug the code inside the function (i.e. step into the function) instead? (If you changed your answer to #3, then that answer is most likely now applicable here.)
    如果下一行代码是一个函数调用,你将使用第 3 题的答案一次性执行整个函数调用。(如果不是,那么第 3 题的答案可能现在不适用了!)你如何告诉 GDB 你想要调试函数内部的代码(即进入函数)?(如果你改变了第 3 题的答案,那么那个答案现在很可能适用于这里。)

    step
    
  5. How do you continue the program after stopping at a breakpoint?
    如何在程序停止在断点后继续执行?

    continue
    
  6. How can you print the value of a variable (or even an expression like 1+2) in gdb?
    你如何在 gdb 中打印变量的值(甚至是一个像 1+2 这样的表达式)?

    expr [表达式]
    
  7. How do you configure gdb so it displays the value of a variable after every step?
    你如何配置 gdb,使其在每一步后显示变量的值?

    这个题中 lldb 和 gdb 好像不是很一样

    (lldb)target stop-hook add -o "expr [变量名]"
    

    表示每次停下断点都需要输出变量的值

  8. How do you show a list of all variables and their values in the current function?
    如何显示当前函数中所有变量及其值?

    frame variable
    
  9. How do you quit out of gdb?
    如何退出 gdb?

    q
    

Exercise 3: Debugging w/ YOU(ser input)

这个练习要求我们实际通过命令调试一个带有输入输出的程序

Exercise 4: Valgrind’ing away

这个练习要求我们使用 Valgrind 进行调试

//segfault_ex.c
int main() {
    int a[20];
    for (int i = 0; ; i++) {
        a[i] = i;
    }
}

先通过 segfault_ex.c 生成 segfault_ex

$ gcc -g -o segfault_ex segfault_ex.c 

运行 segfault_ex 会发现段错误,这是当程序试图访问不属于它的内存时崩溃的结果

$ ./segfault_ex 
Segmentation fault (core dumped)

通过 valgrind 运行 segfault_ex

$ valgrind --tool=memcheck --leak-check=full ./segfault_ex 
==147480== Memcheck, a memory error detector
==147480== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==147480== Using Valgrind-3.25.0 and LibVEX; rerun with -h for copyright info
==147480== Command: ./segfault_ex
==147480== 
==147480== Invalid write of size 4
==147480==    at 0x400114F: main (segfault_ex.c:4)
==147480==  Address 0x1fff001000 is not stack'd, malloc'd or (recently) free'd
==147480== 
==147480== 
==147480== Process terminating with default action of signal 11 (SIGSEGV)
==147480==  Access not within mapped region at address 0x1FFF001000
==147480==    at 0x400114F: main (segfault_ex.c:4)
==147480==  If you believe this happened as a result of a stack
==147480==  overflow in your program's main thread (unlikely but
==147480==  possible), you can try to increase the size of the
==147480==  main thread stack using the --main-stacksize= flag.
==147480==  The main thread stack size used in this run was 8388608.
==147480== 
==147480== HEAP SUMMARY:
==147480==     in use at exit: 0 bytes in 0 blocks
==147480==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==147480== 
==147480== All heap blocks were freed -- no leaks are possible
==147480== 
==147480== For lists of detected and suppressed errors, rerun with: -s
==147480== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)

报错类型如下

==147480== Invalid write of size 4
==147480==    at 0x400114F: main (segfault_ex.c:4)
==147480==  Address 0x1fff001000 is not stack'd, malloc'd or (recently) free'd

下面来看另外一个程序

// no_segfault_ex.c
#include <stdio.h>
int main() {
    int a[5] = {1, 2, 3, 4, 5};
    unsigned total = 0;
    for (int j = 0; j < sizeof(a); j++) {
        total += a[j];
    }
    printf("sum of array is %d\n", total);
}

运行

$ valgrind --tool=memcheck --leak-check=full ./no_segfault_ex 
==149325== Memcheck, a memory error detector
==149325== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==149325== Using Valgrind-3.25.0 and LibVEX; rerun with -h for copyright info
==149325== Command: ./no_segfault_ex
==149325== 
==149325== Conditional jump or move depends on uninitialised value(s)
==149325==    at 0x48DFAD6: __vfprintf_internal (vfprintf-internal.c:1516)
==149325==    by 0x48C979E: printf (printf.c:33)
==149325==    by 0x40011E7: main (no_segfault_ex.c:8)
==149325== 
==149325== Use of uninitialised value of size 8
==149325==    at 0x48C32EB: _itoa_word (_itoa.c:177)
==149325==    by 0x48DEABD: __vfprintf_internal (vfprintf-internal.c:1516)
==149325==    by 0x48C979E: printf (printf.c:33)
==149325==    by 0x40011E7: main (no_segfault_ex.c:8)
==149325== 
==149325== Conditional jump or move depends on uninitialised value(s)
==149325==    at 0x48C32FC: _itoa_word (_itoa.c:177)
==149325==    by 0x48DEABD: __vfprintf_internal (vfprintf-internal.c:1516)
==149325==    by 0x48C979E: printf (printf.c:33)
==149325==    by 0x40011E7: main (no_segfault_ex.c:8)
==149325== 
==149325== Conditional jump or move depends on uninitialised value(s)
==149325==    at 0x48DF5C3: __vfprintf_internal (vfprintf-internal.c:1516)
==149325==    by 0x48C979E: printf (printf.c:33)
==149325==    by 0x40011E7: main (no_segfault_ex.c:8)
==149325== 
==149325== Conditional jump or move depends on uninitialised value(s)
==149325==    at 0x48DEC05: __vfprintf_internal (vfprintf-internal.c:1516)
==149325==    by 0x48C979E: printf (printf.c:33)
==149325==    by 0x40011E7: main (no_segfault_ex.c:8)
==149325== 
sum of array is 1032207083
==149325== 
==149325== HEAP SUMMARY:
==149325==     in use at exit: 0 bytes in 0 blocks
==149325==   total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==149325== 
==149325== All heap blocks were freed -- no leaks are possible
==149325== 
==149325== Use --track-origins=yes to see where uninitialised values come from
==149325== For lists of detected and suppressed errors, rerun with: -s
==149325== ERROR SUMMARY: 23 errors from 5 contexts (suppressed: 0 from 0)

这个程序虽然没有段错误,但产生了严重的内存问题

Conditional jump or move depends on uninitialised value(s)

这句话说明了 未初始化数据影响程序控制流,具体体现在 越界读取垃圾值 → 累加到total → printf使用total → 触发未初始化值错误

其实在这个例子中 sizeof(a) 的值是 20,导致了数组越界

Exercise 5: Pointers and Structures in C

这个练习他给了我们一个算法要求我们实现来判断一个链表是否有环,直接实现即可

#include <stddef.h>
#include "ll_cycle.h"

typedef struct node *LinkedList;


int ll_has_cycle(node *head) { // 来判断链表是否有环
    node *slow = head, *fast = head; // 快慢指针
    while (fast != NULL && fast->next != NULL) { // 快指针和快指针的下一个都不为空
        slow = slow->next; // 慢指针走一步
        fast = fast->next; // 快指针走一步
        if (fast != NULL) // 如果快指针不为空
            fast = fast->next; // 快指针再走一步
        else 
            break; // 如果快指针为空,说明链表末尾了,直接退出
        if (slow == fast) { // 如果相遇了,说明有环
            return 1;
        }
    }
    return 0; // 如果快指针走到链表末尾了,说明没有环
}